#ifndef LW_ARRAY_LIST_H
#define LW_ARRAY_LIST_H

#include <vector>

#include "abstract/RandomAccessList.h"

template<typename T>
class ArrayList : public RandomAccessList<T> {
protected:
    std::vector<T> wrappedList;
	static const std::string emptyAccessMessage;
	static const std::string indexOutOfBoundsMessage;

public:

    ArrayList() {

    }

    virtual ~ArrayList() {
    }

    /**
     * Compares specified list with this list and returns true if they are equal.
     * Two lists are equal if their size is the same, and each member in location i in one list is equal to the the member in location i in the other list.
     */
    virtual inline bool operator==(const ArrayList& v) const {
        return (wrappedList == v.wrappedList);
    }

    /**
     * Compares specified list with this list and returns true if they are not equal.
     * Two lists are equal if their size is the same, and each member in location i in one list is equal to the the member in location i in the other list.
     */
    virtual inline bool operator!=(const ArrayList& v) const {
        return (wrappedList != v.wrappedList);
    }

    /**
     * Appends the specified element to the end of this list.
     */
    virtual inline void Add(const T& e) {
        RandomAccessList<T>::Add(e);
    }

    /**
     * Inserts the specified element at the specified position in this list.
     */
    virtual void Add(int index, const T& e) {
        if (index >= 0 && index <= Size()) {
            typename std::vector<T>::iterator it = wrappedList.begin();
            for (int i = 0; i < index; ++i) {
                ++it;
            }
            wrappedList.insert(it, e);
            return;
        }

        throw std::out_of_range(indexOutOfBoundsMessage);
    }

    /**
     * Inserts the given element at the beginning of this list.
     */
    virtual inline void AddFirst(const T& e) {
        Add(0, e);
    }

    /**
     * Inserts the given element at the end of this list.
     */
    virtual inline void AddLast(const T& e) {
        wrappedList.push_back(e);
    }

    /**
     * Removes all of the elements from this list.
     */
    virtual inline void Clear() {
        wrappedList.clear();
    }

    /**
     * Returns true if this list contains the specified element.
     */
    virtual bool Contains(const T& e) const {
        typename std::vector<T>::const_iterator it = wrappedList.begin();
        while (it != wrappedList.end()) {
            if (*it == e) {
                return true;
            }
            ++it;
        }
        return false;
    }

    /**
     * Sets or returns the element at the specified position in this list.
     */
    virtual inline T& operator[](int index) {
        if (index >= 0 && index < Size()) {
            return wrappedList[index];
        }

        throw std::out_of_range(indexOutOfBoundsMessage);
    }

    /**
     * Returns the element at the specified position in this list.
     */
    virtual inline const T& operator[](int index) const {
        if (index >= 0 && index < Size()) {
            return wrappedList[index];
        }

        throw std::out_of_range(indexOutOfBoundsMessage);
    }

    /**
     * Returns the first element in this list.
     */
    virtual inline const T& GetFirst() const {
		if(!Empty()) {
			return wrappedList.front();
		}

		throw std::runtime_error(emptyAccessMessage);
    }

    /**
     * Returns the last element in this list.
     */
    virtual inline const T& GetLast() const {
		if(!Empty()) {
			return wrappedList.back();
		}

		throw std::runtime_error(emptyAccessMessage);
    }

    /**
     * Returns the first element in this list.
     */
    virtual inline T& GetFirst() {
		if(!Empty()) {
			return wrappedList.front();
		}
		
		throw std::runtime_error(emptyAccessMessage);
    }

    /**
     * Returns the last element in this list.
     */
    virtual inline T& GetLast() {
		if(!Empty()) {
			return wrappedList.back();
		}

		throw std::runtime_error(emptyAccessMessage);
    }

    /**
     * Returns the index in this list of the first occurrence of the specified element, or -1 if the List does not contain this element.
     */
    virtual int IndexOf(const T& e) const {
        typename std::vector<T>::const_iterator it = wrappedList.begin();
        int index = 0;
        while (it != wrappedList.end()) {
            if (*it == e) {
                return index;
            }
            ++it;
            ++index;
        }
        return -1;
    }

    /**
     * Returns the index in this list of the last occurrence of the specified element, or -1 if the list does not contain this element.
     */
    virtual int LastIndexOf(const T& e) const {
        typename std::vector<T>::const_iterator it = wrappedList.end();
        for (int i = Size() - 1; i >= 0; --i) {
            --it;
            if (*it == e) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Returns an iterator to the first element of the list.
     */
    inline typename std::vector<T>::iterator IteratorBegin() const {
        return wrappedList.begin();
    }

    /**
     * Returns an iterator to the end of the list (after the last element).
     */
    inline typename std::vector<T>::iterator IteratorEnd() const {
        return wrappedList.end();
    }

    /**
     * Removes the element at the specified position in this list.
     */
    virtual void RemoveAt(int index) {
        if (index >= 0 && index < Size()) {
            typename std::vector<T>::const_iterator it = wrappedList.begin();
            for (int i = 0; i < index; ++i) {
                ++it;
            }
            wrappedList.erase(it);
            return;
        }

        throw std::out_of_range(indexOutOfBoundsMessage);
    }

    /**
     * Removes all occurrences of v from this list.
     */
    virtual void Remove(const T &v) {
        typename std::vector<T>::const_iterator it = wrappedList.begin();
        while (it != wrappedList.end()) {
            if (*it == v) {
                it = wrappedList.erase(it);
            } else {
                ++it;
            }
        }
    }

    /**
     * Removes the first element from this list.
     */
    virtual inline void RemoveFirst() {
		typename std::vector<T>::const_iterator it = wrappedList.begin();
		wrappedList.erase(it);
    }

    /**
     * Removes the last element from this list.
     */
    virtual inline void RemoveLast() {
		wrappedList.pop_back();
    }

    /**
     * Returns the number of elements in this list.
     */
    virtual inline int Size() const {
        return wrappedList.size();
    }

    /**
     * Returns true if this list has no elements.
     */
    virtual inline bool Empty() const {
        return wrappedList.empty();
    }
};

template<typename T>
const std::string ArrayList<T>::indexOutOfBoundsMessage = "ArrayList (std::vector) index out of range";

template<typename T>
const std::string ArrayList<T>::emptyAccessMessage = "ArrayList (std::vector) is empty";

#endif //LW_ARRAY_LIST_H
